﻿using ICSharpCode.AvalonEdit;
using ICSharpCode.AvalonEdit.CodeCompletion;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Folding;
using LunarSF.SHomeWorkshop.LunarMarkdownEditor.PythonAnalysis;
using LunarSF.SHomeWorkshop.LunarMarkdownEditor.PythonHelp.CompleteKeywords;
using LunarSF.SHomeWorkshop.LunarMarkdownEditor.Utils.NPOIHelp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Threading;

namespace LunarSF.SHomeWorkshop.LunarMarkdownEditor
{
    public class ReporterEditBase : TextEditor
    {
        private static ImageSource newPageIcon;
        private static ImageSource newWidgetIcon;
        private static ImageSource defaultPropertyIcon;
        private static ImageSource propertyIcon;
        private static ImageSource fontIcon;
        private static ImageSource valueIcon;
        private static ImageSource locationIcon;
        private static ImageSource boolIcon;

        static ReporterEditBase()
        {
            newPageIcon = new BitmapImage(new Uri("pack://application:,,,/LunarMarkdownEditor;component/Images/ReporterIcons/NewPage.png"));
            newWidgetIcon = new BitmapImage(new Uri("pack://application:,,,/LunarMarkdownEditor;component/Images/ReporterIcons/NewWidget.png"));
            defaultPropertyIcon = new BitmapImage(new Uri("pack://application:,,,/LunarMarkdownEditor;component/Images/ReporterIcons/DefaultProperties.png"));
            propertyIcon = new BitmapImage(new Uri("pack://application:,,,/LunarMarkdownEditor;component/Images/ReporterIcons/Properties.png"));
            fontIcon = new BitmapImage(new Uri("pack://application:,,,/LunarMarkdownEditor;component/Images/ReporterIcons/Font.png"));
            valueIcon = new BitmapImage(new Uri("pack://application:,,,/LunarMarkdownEditor;component/Images/ReporterIcons/Value.png"));
            locationIcon = new BitmapImage(new Uri("pack://application:,,,/LunarMarkdownEditor;component/Images/ReporterIcons/Location.png"));
            boolIcon = new BitmapImage(new Uri("pack://application:,,,/LunarMarkdownEditor;component/Images/ReporterIcons/Bool.png"));
        }

        public ReporterEditBase()
        {
            this.SnapsToDevicePixels = true;//避免折叠线模糊
            this.Background = Brushes.WhiteSmoke;
            RenderOptions.SetEdgeMode(this, EdgeMode.Aliased);

            this.WordWrap = true;
            this.Options.ShowEndOfLine = false;
            this.Options.ShowSpaces = false;
            this.Options.ShowTabs = false;
            this.Options.ShowColumnRuler = true;
            this.Options.ShowBoxForControlCharacters = true;
            this.ShowLineNumbers = true;

            this.TextArea.SelectionBorder = new Pen(Brushes.Transparent, 1);
            this.TextArea.SelectionBrush = new SolidColorBrush(Color.FromArgb(255, 145, 83, 0));  // Color.FromArgb(255, 240, 106, 0) 是那两条行高亮线色
            this.TextArea.SelectionCornerRadius = 0;

            this.PreviewKeyDown += ReporterEditBase_PreviewKeyDown;

            #region 代码折叠处理Folding            
            if (Globals.MainWindow.SupportFolding)
            {
                // 与主 Markdown 编辑器折叠状态保持一致！
                // 机器性能不行的话，主 Markdown 编辑器难以支持，Python 编辑器也难以支持！
                StartFolding();
            }
            #endregion

            #region 自动完成
            this.TextArea.TextEntered += TextArea_TextEntered;
            this.TextArea.TextEntering += TextArea_TextEntering;
            #endregion

            #region 内置的自动完成列表
            // 添加 Reporter 生成命令
            foreach (var rcmd in ReporterCommandNameManager.Names)
            {
                if (string.IsNullOrEmpty(rcmd.CompletePrefix))
                {
                    innerCompletionData.Add(new CustomCompletionData(rcmd.CommandName, rcmd.CommandDescript == null ? "报表生成命令" : rcmd.CommandDescript, rcmd.CommandName)
                    {
                        Image = GetCommandIcon(rcmd)
                    });
                }
                else
                {
                    innerCompletionData.Add(new CustomCompletionData(rcmd.CompletePrefix + " " + rcmd.CommandName, rcmd.CommandDescript == null ? "报表生成命令" : rcmd.CommandDescript, rcmd.CommandName)
                    {
                        Image = GetCommandIcon(rcmd)
                    });
                }
            }

            // 这里添加的是一些定义在 文本文件 中的关键词。
            if (CompleteInfoManager.SingleManager.CompletionDatas.Count <= 0)
            {
                // CompleteInfoManager.SingleManager.ReloadCompleteKeywordInfos();
                // 报表设计器没有这些“外部”关键词需要载入

                CompleteInfoManager.SingleManager.BuildCompletionDataList();
            }

            #endregion 内置的自动完成列表
        }

        private MiniReportEditor masterReportEditor;

        public MiniReportEditor MasterReportEditor { get => masterReportEditor; set => masterReportEditor = value; }

        private ImageSource GetCommandIcon(ReporterCommandNameManager cmdName)
        {
            switch (cmdName.CommandType)
            {
                case CommandType.DefaultPropertySettingCommand: return defaultPropertyIcon;
                case CommandType.PropertySettingCommand: return propertyIcon;
                case CommandType.NewPageOrAddRowNumberCommand: return newPageIcon;
                case CommandType.NewWidgetCommand: return newWidgetIcon;
                default: return null;
            }
        }

        #region 折叠

        void foldingUpdateTimer_Tick(object sender, EventArgs e)
        {
            if (Globals.MainWindow.SupportFolding == false) return;

            if (foldingStrategy != null && foldingManager != null)
            {
                foldingStrategy.UpdateFoldings(foldingManager, this.Document);
            }
        }

        DispatcherTimer foldingUpdateTimer = new DispatcherTimer();

        public DispatcherTimer FoldingUpdateTimer { get { return this.foldingUpdateTimer; } }

        public CustomFoldingManager FoldingManager
        {
            get { return foldingManager; }
        }

        CustomFoldingManager foldingManager = null;
        AbstractFoldingStrategy foldingStrategy = null;

        public AbstractFoldingStrategy FoldingStrategy
        {
            get { return foldingStrategy; }
        }

        public bool StopFolding()
        {
            try
            {
                if (foldingUpdateTimer.IsEnabled)
                {
                    foldingUpdateTimer.Stop();
                }

                if (foldingManager != null)
                {
                    CustomFoldingManager.Uninstall(foldingManager);
                }
                foldingUpdateTimer.Tick -= foldingUpdateTimer_Tick;

                return true;
            }
            catch
            {
                return false;
            }
        }

        public bool StartFolding()
        {
            try
            {
                if (foldingStrategy == null)
                    foldingStrategy = new CustomFoldingStrategyForReporter(this);

                foldingManager = CustomFoldingManager.Install(this.TextArea);

                foldingStrategy.UpdateFoldings(FoldingManager, this.Document);

                foldingUpdateTimer.Interval = TimeSpan.FromSeconds(0.05);//0.05=50毫秒。
                foldingUpdateTimer.Tick += foldingUpdateTimer_Tick;

                if (foldingUpdateTimer.IsEnabled == false)
                {
                    foldingUpdateTimer.Start();
                }
                return true;
            }
            catch
            {
                return false;
            }
        }


        /// <summary>
        /// 折叠插入点所在的可折叠区域。
        /// </summary>
        public void FoldSelectedBlock()
        {
            if (Globals.MainWindow.SupportFolding == false) return;

            ICSharpCode.AvalonEdit.Folding.FoldingSection folding = null;

            foreach (var i in this.FoldingManager.AllFoldings)
            {
                if (i.StartOffset <= this.SelectionStart &&
                    i.EndOffset >= this.SelectionStart + this.SelectionLength)
                {
                    if (folding == null)
                    {
                        folding = i;
                        continue;
                    }
                    else
                    {
                        if (folding.StartOffset < i.StartOffset)
                        {
                            folding = i;
                            continue;
                        }
                    }
                }
            }

            if (folding != null)
            {
                folding.IsFolded = !folding.IsFolded;
            }
        }

        #endregion

        #region 自动完成

        private List<ICompletionData> innerCompletionData = new List<ICompletionData>();

        public List<ICompletionData> InnerCompletionData
        {
            get { return innerCompletionData; }
        }

        public void HideCompleteWindow()
        {
            if (completionWindow == null) return;

            CompletionWindow.Close();
            completionWindow = null;
        }

        private void TextArea_TextEntering(object sender, TextCompositionEventArgs e)
        {
            try
            {

                if ((e.Text.Length == 1 && ";:；：[［【".Contains(e.Text)) &&
                    CompletionWindow != null && CompletionWindow.Visibility == Visibility.Visible)
                {
                    var selItem = CompletionWindow.CompletionList.SelectedItem;
                    if (selItem != null)
                    {
                        var line = this.Document.GetLineByOffset(this.SelectionStart);
                        if (line == null) return;
                        var currentOffsetInLine = this.SelectionStart - line.Offset;
                        var lineLeftText = this.Document.GetText(line.Offset, currentOffsetInLine);

                        var tailSpaceCount = 0;
                        for (int i = lineLeftText.Length - 1; i >= 0; i--)
                        {
                            if (lineLeftText[i] == ' ') { tailSpaceCount++; }
                            else break;
                        }

                        if (tailSpaceCount > 0)
                        {
                            lineLeftText = lineLeftText.TrimEnd(new char[] { ' ', });
                        }

                        var lastIndexOfInvalidateChar = -1;
                        for (int i = lineLeftText.Length - 1; i >= 0; i--)
                        {
                            var c = lineLeftText[i];
                            // if (c == '.') continue;  // 这句不能加，否则会把 . 之前的也替换掉。
                            if (IsValidateChar(c)) continue;

                            lastIndexOfInvalidateChar = i;
                            break;
                        }

                        var start = this.SelectionStart;
                        var length = this.SelectionLength;
                        if (lastIndexOfInvalidateChar >= 0)
                        {
                            start = line.Offset + lastIndexOfInvalidateChar + 1;
                            length = lineLeftText.Length - lastIndexOfInvalidateChar - 1 + tailSpaceCount;
                        }
                        else
                        {
                            var leftWhiteCharsCount = 0;
                            foreach (var lc in lineLeftText)
                            {
                                if (lc == ' ' || lc == '\t')
                                    leftWhiteCharsCount++;
                                else break;
                            }

                            start = line.Offset + leftWhiteCharsCount;
                            length = lineLeftText.Length - leftWhiteCharsCount;
                        }

                        if (left_text.StartsWith(";"))
                        {
                            var dline = this.Document.GetLineByOffset(this.SelectionStart);
                            this.Document.Replace(dline.Offset, 1, "；");
                        }

                        selItem.Complete(this.TextArea, new AnchorSegment(this.TextArea.Document, start, length), e);

                        // 加个分隔符
                        this.TextArea.Document.Insert(this.SelectionStart, "：");

                        completionWindow?.Close();
                        completionWindow = null;
                        e.Handled = true;

                        if (":：[［【{｛,，".Contains(e.Text))
                        {
                            ShowCompleteWindow(e);
                        }

                        return;
                    }

                    if (e.Text == ";" || e.Text == "；")
                    {
                        ShowCompleteWindow(e);
                    }

                    return;
                }
            }
            catch (Exception ex)
            {
                LMessageBox.ShowWarning(ex.Message);
            }
        }

        private static List<CustomCompletionData> fontNameCompletionItems = null;

        public static List<CustomCompletionData> FontNameCompletionItems
        {
            get { return FontNameCompletionItems; }
        }

        private bool ShowReporterCommandCompletion(DocumentLine line, string lineLeftText, string inputText, ref IList<ICompletionData> data)
        {
            // 从当前行的前一行开始向前倒找
            if (line.LineNumber >= 1)
            {
                data.Clear();

                if (inputText == ";" || inputText == "；")
                {
                    // Reporter 生成命令
                    foreach (var innerItem in innerCompletionData)
                    {
                        data.Add(innerItem);
                    }
                }
                else if (inputText == ":" || inputText == "：")
                {
                    //var newData = new CustomCompletionData(ipm.Value, "文本片段或变量名", ipm.Value);
                    //data.Add(newData);
                    // Reporter 命令的可用属性
                    var leftTxt = LeftText;

                    if ((leftTxt.EndsWith(":") || leftTxt.EndsWith("：")))  // 这个冒号是正在输入的
                    {
                        var leftTxtHeader = leftTxt.Substring(0, leftTxt.Length - 1);
                        if (leftTxtHeader.Contains(":") || leftTxtHeader.Contains("："))
                        {
                            return false;   // 出现第二个冒号时，不提示。
                        }
                    }

                    leftTxt = leftTxt.Trim(new char[] { ' ', '　', '\t', ';', '；', ':', '：' });

                    // 命令的值一共只有以下类型：
                    // 1. 页面尺寸（特殊的字符串标记，例如“A4”，只能选择支持的种类之一）。
                    // 2. 页面方向（特殊的字符串标记，只能选择“纵向”与“横向”之一）。
                    // 3. 色彩标记（只支持内置色彩名称之一）。
                    // 4. 对齐方式（只支持“左对齐”“居中”“右对齐”三个值之一，仅支持水平对齐）。
                    // 5. 线型标记（只支持“点线”“划线”“点划线”“点点划线”“实线”五个值之一，“点划线”“点点划线”不能提供有效支持）。
                    // 6. “新”字开头的部件创建命令标记（不包含“新页面”命令标记），它们需要提示坐标。
                    // 7. 字体（提供当前系统中安装的所有字体的名称列表，用户可以选择其一）。
                    // 8. 一些特殊的格式命令，它们的值必定是“真”或“假”。
                    // 9. 命令没有值（或值为数字或不可识别的纯文本）的不需要智能提示。
                    // 10. 条码或二维码类型。

                    if (leftTxt.Contains("页面尺寸"))
                    {
                        var i = 1;
                        foreach (var pi in PaperInfo.PaperInfos)
                        {
                            var newData = new CustomCompletionData(i.ToString() + " " + pi.PaperTypeName, "纸型标记", pi.PaperTypeName)
                            {
                                Image = valueIcon,
                            };
                            data.Add(newData);
                            i++;
                        }
                    }
                    else if (leftTxt.Contains("方向"))
                    {
                        data.Add(new CustomCompletionData("1 横向", "页面朝向标记", "横向")
                        {
                            Image = valueIcon,
                        });
                        data.Add(new CustomCompletionData("2 纵向", "页面朝向标记", "纵向")
                        {
                            Image = valueIcon,
                        });
                    }
                    else if (leftTxt.Contains("色"))
                    {
                        var i = 1;
                        foreach (var bsh in BrushManager.BrushesList)
                        {
                            var newData = new CustomCompletionData(i.ToString() + " " + bsh.CnName, $"支持的色彩标记：{bsh.EnName}", bsh.CnName);
                            newData.Image = IconGenerator.GenerateIcon(16, 16, bsh.Brush);  // 色彩图标自动生成，不用 valueIcon
                            data.Add(newData);
                            i++;
                        }
                    }
                    else if (leftTxt.Contains("对齐"))
                    {
                        data.Add(new CustomCompletionData("1 左对齐", "对齐方式", "左对齐")
                        {
                            Image = valueIcon,
                        });
                        data.Add(new CustomCompletionData("2 居中", "对齐方式", "居中")
                        {
                            Image = valueIcon,
                        });
                        data.Add(new CustomCompletionData("3 右对齐", "对齐方式", "右对齐")
                        {
                            Image = valueIcon,
                        });
                    }
                    else if (leftTxt.Contains("线型"))
                    {
                        data.Add(new CustomCompletionData("1 点线", "线型", "点线")
                        {
                            Image = valueIcon,
                        });
                        data.Add(new CustomCompletionData("2 划线", "线型", "划线")
                        {
                            Image = valueIcon,
                        });
                        data.Add(new CustomCompletionData("3 点划线", "线型", "点划线")
                        {
                            Image = valueIcon,
                        });
                        data.Add(new CustomCompletionData("4 点点划线", "线型", "点点划线")
                        {
                            Image = valueIcon,
                        });
                        data.Add(new CustomCompletionData("5 实线", "线型", "实线")
                        {
                            Image = valueIcon,
                        });
                    }
                    else if (leftTxt.StartsWith("新") && leftTxt.StartsWith("新页面") == false &&
                             leftTxt.Contains("边栏") == false && leftTxt.Contains("新页码") == false)
                    {
                        var suffix = "";
                        if (leftTxt.StartsWith("新网格"))
                        {
                            suffix = "_{1, 1}";
                        }

                        // 内容部件才支持自动列号
                        if (leftTxt.StartsWith("新字符行"))
                        {
                            var cord1 = new CustomCompletionData("1 （_，_）" + suffix, "（自动行号，自动列号，1行，自动列数）", "（_，_）" + suffix)
                            {
                                Suffix = suffix,
                                BackSelectOffset = 0,
                                Image = locationIcon,
                            };
                            data.Add(cord1);

                            var cord2 = new CustomCompletionData("2 （行号，_）" + suffix, "（指定行号，自动列号，1行，自动列数）", "（，_）" + suffix)
                            {
                                Suffix = suffix,
                                BackSelectOffset = 3,
                                Image = locationIcon,
                            };
                            data.Add(cord2);

                            var cord3 = new CustomCompletionData("2 （_，列号）" + suffix, "（自动行号，指定列号，1行，自动列数）", "（_，）" + suffix)
                            {
                                Suffix = suffix,
                                BackSelectOffset = 1,
                                Image = locationIcon,
                            };
                            data.Add(cord3);
                        }
                        else if (leftTxt.StartsWith("新字符列"))
                        {
                            var cord1 = new CustomCompletionData("1 （_，_）" + suffix, "（自动行号，自动列号，自动行数，1列）", "（_，_）" + suffix)
                            {
                                Suffix = suffix,
                                BackSelectOffset = 0,
                                Image = locationIcon,
                            };
                            data.Add(cord1);

                            var cord2 = new CustomCompletionData("2 （_，列号）" + suffix, "（自动行号，指定列号，自动行数，1列）", "（_，）" + suffix)
                            {
                                Suffix = suffix,
                                BackSelectOffset = 1,
                                Image = locationIcon,
                            };
                            data.Add(cord2);

                            var cord3 = new CustomCompletionData("2 （行号，_）" + suffix, "（指定行号，自动列号，自动行数，1列）", "（，_）" + suffix)
                            {
                                Suffix = suffix,
                                BackSelectOffset = 3,
                                Image = locationIcon,
                            };
                            data.Add(cord3);
                        }
                        else
                        {
                            var cord1 = new CustomCompletionData("1 （_，_）" + suffix, "（自动行号，自动列号，1行，自动列数）", "（_，_）" + suffix)
                            {
                                Suffix = suffix,
                                BackSelectOffset = 0,
                                Image = locationIcon,
                            };
                            data.Add(cord1);

                            var cord2 = new CustomCompletionData("2 （列号）" + suffix, "（自动行号，指定列号，1行，自动列数）", "（）" + suffix)
                            {
                                Suffix = suffix,
                                BackSelectOffset = 1,
                                Image = locationIcon,
                            };
                            data.Add(cord2);

                            var cord3 = new CustomCompletionData("3 （_，列号）" + suffix, "（自动行号，指定列号，1行，自动列数）", "（_，）" + suffix)
                            {
                                Suffix = suffix,
                                BackSelectOffset = 1,
                                Image = locationIcon,
                            };
                            data.Add(cord3);

                            var cord4 = new CustomCompletionData("4 （行号，列号）" + suffix, "（指定行号，指定列号，1行，自动列数）", "（，，）" + suffix)
                            {
                                Suffix = suffix,
                                BackSelectOffset = 2,
                                Image = locationIcon,
                            };
                            data.Add(cord4);
                        }
                    }
                    else if (leftTxt.StartsWith("新边栏") || leftTxt.Contains("新页码"))   // 例如：“；新边栏文本：（1）文本内容”。
                    {
                        // 边栏坐标很简单只提供位置即可。
                        // 页面四边分为 12 个区域，页眉区域分三个子区域，从左到右标号为 1-3；
                        //                         页脚区域分三个子区域，从左到右标号为 4-6；
                        //                         左边栏分三个子区域，从上到下标号为 7-9；
                        //                         右边栏分三个子区域，从上到下标号为 10-12。
                        // 部件在边栏时，可以只占一个区域，也可以“占满”整个边栏。

                        var cord1 = new CustomCompletionData("（1）", "（页眉左，占一个边栏区域）", "（1）")
                        {
                            BackSelectOffset = 0,
                            Image = locationIcon,
                        };
                        data.Add(cord1);

                        var cord1e = new CustomCompletionData("（1，_）", "（页眉左，占满页眉区域并左齐）", "（1，_）")
                        {
                            BackSelectOffset = 0,
                            Image = locationIcon,
                        };
                        data.Add(cord1e);

                        var cord2 = new CustomCompletionData("（2）", "（页眉中，占一个边栏区域）", "（2）")
                        {
                            BackSelectOffset = 0,
                            Image = locationIcon,
                        };
                        data.Add(cord2);

                        var cord2e = new CustomCompletionData("（2，_）", "（页眉中，占满页眉区域并居中）", "（2，_）")
                        {
                            BackSelectOffset = 0,
                            Image = locationIcon,
                        };
                        data.Add(cord2e);

                        var cord3 = new CustomCompletionData("（3）", "（页眉右，占一个边栏区域）", "（3）")
                        {
                            BackSelectOffset = 0,
                            Image = locationIcon,
                        };
                        data.Add(cord3);

                        var cord3e = new CustomCompletionData("（3，_）", "（页眉右，占满页眉区域并右齐）", "（3，_）")
                        {
                            BackSelectOffset = 0,
                            Image = locationIcon,
                        };
                        data.Add(cord3e);

                        var cord4 = new CustomCompletionData("（4）", "（页脚左，占一个边栏区域）", "（4）")
                        {
                            BackSelectOffset = 0,
                            Image = locationIcon,
                        };
                        data.Add(cord4);

                        var cord4e = new CustomCompletionData("（4，_）", "（页脚左，占满页脚区域并左齐）", "（4，_）")
                        {
                            BackSelectOffset = 0,
                            Image = locationIcon,
                        };
                        data.Add(cord4e);

                        var cord5 = new CustomCompletionData("（5）", "（页脚中，占一个边栏区域）", "（5）")
                        {
                            BackSelectOffset = 0,
                            Image = locationIcon,
                        };
                        data.Add(cord5);

                        var cord5e = new CustomCompletionData("（5，_）", "（页脚中，占满页脚区域并居中）", "（5，_）")
                        {
                            BackSelectOffset = 0,
                            Image = locationIcon,
                        };
                        data.Add(cord5e);

                        var cord6 = new CustomCompletionData("（6）", "（页脚右，占一个边栏区域）", "（6）")
                        {
                            BackSelectOffset = 0,
                            Image = locationIcon,
                        };
                        data.Add(cord6);

                        var cord6e = new CustomCompletionData("（6，_）", "（页脚右，占满页脚区域并右齐）", "（6，_）")
                        {
                            BackSelectOffset = 0,
                            Image = locationIcon,
                        };
                        data.Add(cord6e);

                        var cord7 = new CustomCompletionData("（7）", "（左边栏上，占一个边栏区域）", "（7）")
                        {
                            BackSelectOffset = 0,
                            Image = locationIcon,
                        };
                        data.Add(cord7);

                        var cord7e = new CustomCompletionData("（7，_）", "（左边栏上，占满左边栏区域并上齐）", "（7，_）")
                        {
                            BackSelectOffset = 0,
                            Image = locationIcon,
                        };
                        data.Add(cord7e);

                        var cord8 = new CustomCompletionData("（8）", "（左边栏中，占一个边栏区域）", "（8）")
                        {
                            BackSelectOffset = 0,
                            Image = locationIcon,
                        };
                        data.Add(cord8);

                        var cord8e = new CustomCompletionData("（8，_）", "（左边栏中，占满左边栏区域并居中）", "（8，_）")
                        {
                            BackSelectOffset = 0,
                            Image = locationIcon,
                        };
                        data.Add(cord8e);

                        var cord9 = new CustomCompletionData("（9）", "（左边栏下，占一个边栏区域）", "（9）")
                        {
                            BackSelectOffset = 0,
                            Image = locationIcon,
                        };
                        data.Add(cord9);

                        var cord9e = new CustomCompletionData("（9，_）", "（左边栏下，占满左边栏区域并下齐）", "（9，_）")
                        {
                            BackSelectOffset = 0,
                            Image = locationIcon,
                        };
                        data.Add(cord9e);

                        var cord10 = new CustomCompletionData("（10）", "（右边栏上，占一个边栏区域）", "（10）")
                        {
                            BackSelectOffset = 0,
                            Image = locationIcon,
                        };
                        data.Add(cord10);

                        var cord10e = new CustomCompletionData("（10，_）", "（右边栏上，占满右边栏区域并上齐）", "（10，_）")
                        {
                            BackSelectOffset = 0,
                            Image = locationIcon,
                        };
                        data.Add(cord10e);

                        var cord11 = new CustomCompletionData("（11）", "（右边栏中，占一个边栏区域）", "（11）")
                        {
                            BackSelectOffset = 0,
                            Image = locationIcon,
                        };
                        data.Add(cord11);

                        var cord11e = new CustomCompletionData("（11，_）", "（右边栏中，占满右边栏区域并居中）", "（11，_）")
                        {
                            BackSelectOffset = 0,
                            Image = locationIcon,
                        };
                        data.Add(cord11e);

                        var cord12 = new CustomCompletionData("（12）", "（右边栏中，占一个边栏区域）", "（12）")
                        {
                            BackSelectOffset = 0,
                            Image = locationIcon,
                        };
                        data.Add(cord12);

                        var cord12e = new CustomCompletionData("（12，_）", "（右边栏中，占满右边栏区域并下齐）", "（12，_）")
                        {
                            BackSelectOffset = 0,
                            Image = locationIcon,
                        };
                        data.Add(cord12e);
                    }
                    else if (leftTxt.Contains("字体"))
                    {
                        if (fontNameCompletionItems == null || fontNameCompletionItems.Count == 0)
                        {
                            fontNameCompletionItems = new List<CustomCompletionData>();

                            if (Globals.MainWindow.FontFamilyCollection != null)
                            {
                                var familyCollection = Globals.MainWindow.FontFamilyCollection;
                                FontFamilyListItem[] items = new FontFamilyListItem[familyCollection.Count];

                                int i = 0;

                                foreach (FontFamily family in familyCollection)
                                {
                                    items[i++] = new FontFamilyListItem(family);
                                }

                                Array.Sort<FontFamilyListItem>(items);

                                var ifont = 1;
                                foreach (FontFamilyListItem item in items)
                                {
                                    fontNameCompletionItems.Add(new CustomCompletionData(ifont.ToString() + " " + item.Text, "字体名", item.Text)
                                    {
                                        Image = fontIcon,
                                    });
                                    ifont++;
                                }
                            }
                        }

                        if (fontNameCompletionItems.Count > 0)
                        {
                            foreach (var item in fontNameCompletionItems)
                            {
                                data.Add(item);
                            }
                        }
                    }
                    else if (leftTxt.Contains("边栏显示模式"))
                    {
                        var d1 = new CustomCompletionData("1 全部页", "全部页")
                        {
                            BackSelectOffset = 0,
                            Image = propertyIcon,
                        };
                        data.Add(d1);

                        var d2 = new CustomCompletionData("2 奇数页", "奇数页")
                        {
                            BackSelectOffset = 0,
                            Image = propertyIcon,
                        };
                        data.Add(d2);

                        var d3 = new CustomCompletionData("3 偶数页", "偶数页")
                        {
                            BackSelectOffset = 0,
                            Image = propertyIcon,
                        };
                        data.Add(d3);
                    }
                    else if (LeftText.Contains("边栏开关"))
                    {
                        var d1 = new CustomCompletionData("1 （x）指定区域", "指定边栏12个区域之一开或关", "（）")
                        {
                            BackSelectOffset = 1,
                            Image = propertyIcon,
                        };
                        data.Add(d1);

                        var d2 = new CustomCompletionData("2 （*）所有区域", "指定所有12个边栏区域开或关", "（*）")
                        {
                            BackSelectOffset = 0,
                            Image = propertyIcon,
                        };
                        data.Add(d2);

                        var d3 = new CustomCompletionData("3 （左）边栏", "指定左边栏3个子区域开或关", "（左）")
                        {
                            BackSelectOffset = 0,
                            Image = propertyIcon,
                        };
                        data.Add(d3);


                        var d4 = new CustomCompletionData("3 （上）边栏", "指定左边栏3个子区域开或关", "（上）")
                        {
                            BackSelectOffset = 0,
                            Image = propertyIcon,
                        };
                        data.Add(d4);

                        var d5 = new CustomCompletionData("3 （右）边栏", "指定左边栏3个子区域开或关", "（右）")
                        {
                            BackSelectOffset = 0,
                            Image = propertyIcon,
                        };
                        data.Add(d5);

                        var d6 = new CustomCompletionData("3 （下）边栏", "指定左边栏3个子区域开或关", "（下）")
                        {
                            BackSelectOffset = 0,
                            Image = propertyIcon,
                        };
                        data.Add(d6);

                    }
                    else
                    {
                        switch (leftTxt)
                        {
                            case "默认加粗":
                            case "默认倾斜":
                            case "默认下划线":
                            case "默认删除线":
                            case "默认自动换行":
                            case "字段默认加粗":
                            case "字段默认倾斜":
                            case "字段默认下划线":
                            case "字段默认自动换行":
                            case "字符行列填涂模式":
                            case "加粗":
                            case "倾斜":
                            case "下划线":
                            case "删除线":
                            case "自动换行":
                                {
                                    data.Add(new CustomCompletionData("1 真", "逻辑值", "真") { Image = boolIcon, });
                                    data.Add(new CustomCompletionData("2 假", "逻辑值", "假") { Image = boolIcon, });
                                    break;
                                }
                            case "显示辅助线":
                                {
                                    data.Add(new CustomCompletionData("1 是", "逻辑值", "是") { Image = boolIcon, });
                                    data.Add(new CustomCompletionData("2 否", "逻辑值", "否") { Image = boolIcon, });
                                    break;
                                }
                            case "填涂模式":
                                {
                                    data.Add(new CustomCompletionData("1 关", "逻辑值：“字符行”“字符列”关闭填涂模式", "关") { Image = boolIcon, });
                                    data.Add(new CustomCompletionData("2 开", "逻辑值：“字符行”“字符列”开启填涂模式（默认开启）", "开") { Image = boolIcon, });
                                    break;
                                }
                        }
                    }
                }
                else if (inputText == "[" || inputText == "【" || inputText == "［")
                {
                    var leftTxt = LeftText.Trim(new char[] { ' ', '　', '\t', ';', '；', ':', '：' });

                    if (leftTxt.StartsWith("新二维码") || leftTxt.StartsWith("新二维码字段"))
                    {
                        var newDefaultData = new CustomCompletionData("1 []", "二维码", "]")
                        {
                            BackSelectOffset = 0,
                            Image = valueIcon,
                        };
                        data.Add(newDefaultData);

                        var barCodeNames = "39|128|matrix|pdf417|ean13|aztec|codabar|code93|ean8|itf|upca|upce|plessey".Split(new char[] { '|' }, StringSplitOptions.None);
                        for (int i = 0; i < barCodeNames.Length; i++)
                        {
                            var newData = new CustomCompletionData($"{(i + 2).ToString()} [{barCodeNames[i]}条形码]", "支持的二维码或条码类型", $"{barCodeNames[i]}]")
                            {
                                BackSelectOffset = 0,
                                Image = valueIcon,
                            };
                            data.Add(newData);
                        }
                    }
                }
                else if (inputText == "{" || inputText == "｛")
                {
                    // 可能正在输入复合字段列表
                    var leftTxt = LeftText.Trim(new char[] { ' ', '　', '\t', '{', '｛' });

                    if (leftTxt.Contains("字段") && (leftTxt.EndsWith(")") || leftTxt.EndsWith("）")))
                    {
                        var txtTable = masterReportEditor.DataContextTable as 文字表;
                        if (txtTable != null)
                        {
                            if (txtTable.文字表头行集 != null && txtTable.文字表头行集.Count > 0)
                            {
                                var fieldNames = txtTable.文字表头行集[txtTable.文字表头行集.Count - 1].文字表单元格集;
                                var fieldNumber = 1;
                                foreach (var fieldCell in fieldNames)
                                {
                                    var newData = new CustomCompletionData($"{fieldNumber} {fieldCell.文字表单元格文本}", "可用字段名", fieldCell.文字表单元格文本)
                                    {
                                        Image = valueIcon,
                                    };
                                    data.Add(newData);
                                    fieldNumber++;
                                }
                            }
                        }
                    }
                    else if (leftTxt.Contains("新文本") || leftTxt.Contains("新边栏文本") || leftTxt.Contains("新页码"))  // 注意：不能使用文本字段部件！
                    {
                        // 添加页码自动提示

                        var pnN1 = new CustomCompletionData("1 {x}", "半角数字页码", "x}")
                        {
                            Image = valueIcon,
                        };
                        data.Add(pnN1);

                        var pnN3 = new CustomCompletionData("2 {-x-}", "半角数字页码", "-x-}")
                        {
                            Image = valueIcon
                        };
                        data.Add(pnN3);

                        var pnN5 = new CustomCompletionData("3 {第x页}", "半角数字页码", "第x页}")
                        {
                            Image = valueIcon,
                        };
                        data.Add(pnN5);

                        var pnN7 = new CustomCompletionData("4 {第x页，共y页}", "半角数字页码", "第x页，共y页}")
                        {
                            Image = valueIcon,
                        };
                        data.Add(pnN7);

                        var pnN2 = new CustomCompletionData("5 {X}", "全角数字页码", "X}")
                        {
                            Image = valueIcon,
                        };
                        data.Add(pnN2);

                        var pnN4 = new CustomCompletionData("6 {—X—}", "全角数字页码", "—X—}")
                        {
                            Image = valueIcon,
                        };
                        data.Add(pnN4);

                        var pnN6 = new CustomCompletionData("7 {第X页}", "全角数字页码", "第X页}")
                        {
                            Image = valueIcon,
                        };
                        data.Add(pnN6);

                        var pnN8 = new CustomCompletionData("8 {第X页，共Y页}", "全角数字页码", "第X页，共Y页}")
                        {
                            Image = valueIcon,
                        };
                        data.Add(pnN8);
                    }
                    else if (leftTxt.Contains("边栏开关"))
                    {
                        var switch_on = new CustomCompletionData("1 {开}", "开启指定边栏区域", "开}")
                        {
                            Image = valueIcon,
                        };
                        data.Add(switch_on);

                        var switch_off = new CustomCompletionData("2 {关}", "关闭指定边栏区域", "关}")
                        {
                            Image = valueIcon
                        };
                        data.Add(switch_off);
                    }
                }
                else if (inputText == "," || inputText == "，")
                {
                    // 仍然可能在输入复合字段列表
                    var leftTxt = LeftText;

                    if (leftTxt.Contains("字段") && (leftTxt.Contains("{") || leftTxt.Contains("｛")))
                    {
                        var txtTable = masterReportEditor.DataContextTable as 文字表;
                        if (txtTable != null)
                        {
                            if (txtTable.文字表头行集 != null && txtTable.文字表头行集.Count > 0)
                            {
                                var fieldNames = txtTable.文字表头行集[txtTable.文字表头行集.Count - 1].文字表单元格集;
                                var fieldNumber = 1;
                                foreach (var fieldCell in fieldNames)
                                {
                                    var newData = new CustomCompletionData($"{fieldNumber} {fieldCell.文字表单元格文本}", "可用字段名", fieldCell.文字表单元格文本)
                                    {
                                        Image = valueIcon,
                                    };
                                    data.Add(newData);
                                    fieldNumber++;
                                }
                            }
                        }
                    }
                }

                if (data.Count > 0)
                {
                    for (int i = 0; i < data.Count; i++)
                    {
                        var ci = data[i] as CustomCompletionData;
                        if (ci == null) continue;

                        ci.Completed += Cord_Completed;
                    }

                    ShowCompletionWindow();
                    return true;
                }

                return false;
            }

            return false;
        }

        private void Cord_Completed(object sender, EventArgs e)
        {
            // 完成事件
            try
            {
                var leftTxt = left_text;
                if (leftTxt != null && leftTxt.StartsWith(";"))
                {
                    var docLine = this.Document.GetLineByOffset(this.SelectionStart);
                    if (docLine != null)
                    {
                        this.Document.Replace(docLine.Offset, 1, "；");
                    }
                }

                var item = sender as CustomCompletionData;
                if (item.BackSelectOffset.HasValue && item.BackSelectOffset.Value < item.CompleteText.Length)
                {
                    if (string.IsNullOrEmpty(item.Suffix))
                    {
                        this.Select(this.SelectionStart - item.BackSelectOffset.Value, 0);
                    }
                    else
                    {
                        this.Select(this.SelectionStart - item.BackSelectOffset.Value - item.Suffix.Length, 0);
                    }
                }
            }
            catch (Exception ex)
            {
                LMessageBox.Show(ex.Message);
            }
        }

        private void ShowCompletionWindow()
        {
            if (enableAutoComplete)
            {
                CompletionWindow.Show();
                CompletionWindow.Closed += delegate
                {
                    completionWindow = null;
                };
            }
        }

        private void TextArea_TextEntered(object sender, TextCompositionEventArgs e)
        {
            try
            {
                if (e.Text.Length == 1)
                {
                    ShowCompleteWindow(e);
                }
                else
                {
                    if (completionWindow != null)
                    {
                        completionWindow.Close();
                        completionWindow = null;
                    }
                }
            }
            catch (Exception ex)
            {
                LMessageBox.ShowWarning(ex.Message);
            }
            // Do not set e.Handled=true.
            // We still want to insert the character that was typed.
        }

        private void ShowCompleteWindow(TextCompositionEventArgs e)
        {
            if (IsValidateChars(e.Text) == false)
            {
                var line = this.Document.GetLineByOffset(this.SelectionStart);
                if (line == null) return;

                var currentOffsetInLine = this.SelectionStart - line.Offset;
                var lineLeftText = this.Document.GetText(line.Offset, currentOffsetInLine);

                if (lineLeftText.StartsWith("#") || lineLeftText.Trim(new char[] { ' ', '\t', }).Length <= 0) return;

                completionWindow = new CompletionWindow(this.TextArea)
                {
                    MinHeight = 34,
                    MinWidth = 260,
                };

                IList<ICompletionData> data = CompletionWindow.CompletionList.CompletionData;

                // 根据情况生成自动完成窗口及其中的项目
                if (ShowReporterCommandCompletion(line, lineLeftText, e.Text, ref data)) return;

                if (data.Count > 0)
                {
                    // 完成插入，销毁自动完成窗口
                    CompletionWindow.CompletionList.RequestInsertion(e);
                    CompletionWindow.Close();
                    completionWindow = null;
                }
                else
                {
                    if (CompletionWindow != null)
                    {
                        CompletionWindow.Hide();
                    }
                }
                return;
            }
        }

        /// <summary>
        /// 是否均为可用标识符。包括 _ 字母 英文 中文字符(Unicode 19968-40869)。
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        private bool IsValidateChars(string text)
        {
            if (string.IsNullOrWhiteSpace(text)) return false;
            if (text.Contains('\r') || text.Contains('\n') || text.Contains('\t') || text.Contains(' ')) return false;

            foreach (var c in text)
            {
                if (c == '_') continue;
                if (c >= '0' && c <= '9') continue;
                if (c >= 'a' && c <= 'z') continue;
                if (c >= 'A' && c <= 'Z') continue;
                var v = (int)c;
                if (v >= 19968 && v <= 40869) continue;  // \u4E00-\u9FA5

                return false;
            }

            return true;
        }

        /// <summary>
        /// 是否英文字母、数字、下划线字符或 \u4E00-\u9FA5 之间的汉字。
        /// </summary>
        /// <param name="c"></param>
        /// <returns></returns>
        private bool IsValidateChar(char c)
        {
            if (c == '_') return true;
            if (c >= '0' && c <= '9') return true;
            if (c >= 'a' && c <= 'z') return true;
            if (c >= 'A' && c <= 'Z') return true;
            var v = (int)c;
            if (v >= 19968 && v <= 40869) return true;  // \u4E00-\u9FA5

            return false;
        }

        private void ReporterEditBase_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            bool isCtrl = false ||
                (Keyboard.GetKeyStates(Key.RightCtrl) & KeyStates.Down) > 0 ||
                (Keyboard.GetKeyStates(Key.LeftCtrl) & KeyStates.Down) > 0;

            bool isShift = false ||
                (Keyboard.GetKeyStates(Key.RightShift) & KeyStates.Down) > 0 ||
                (Keyboard.GetKeyStates(Key.LeftShift) & KeyStates.Down) > 0;

            bool isAlt = false ||
                (Keyboard.GetKeyStates(Key.RightAlt) & KeyStates.Down) > 0 ||
                (Keyboard.GetKeyStates(Key.LeftAlt) & KeyStates.Down) > 0;

            try
            {
                switch (e.Key)
                {
                    case Key.I://为什么在这里处理？因为avalonEdit已经绑死Alt+Up，使用Alt+Up不起任何作用。其实用覆盖 OnKeyDown() 方法的办法可以解决。
                        {
                            if (isCtrl)//移动到上一行的上方。
                            {
                                if (isShift)
                                {
                                    if (!isAlt) SwapWithPreviewLine();
                                }
                                e.Handled = true;
                            }
                            break;
                        }
                    case Key.K://为什么用这个？因为avalonEdit已经绑死Alt+Up，使用Alt+Up不起任何作用。其实用覆盖 OnKeyDown() 方法的办法可以解决。
                        {
                            if (isCtrl && isShift && !isAlt)//移动到下一行的下方。
                            {
                                SwapWithNextLine();

                                e.Handled = true;
                            }
                            break;
                        }
                    case Key.Back://BackSpace，退格键
                        {
                            if (!isCtrl && !isAlt)
                            {
                                if (isShift)
                                {
                                    //删除到行首
                                    var textArea = this.TextArea;
                                    if (textArea != null && textArea.IsKeyboardFocused)
                                    {
                                        var curLine = this.Document.GetLineByOffset(this.SelectionStart);
                                        var curLineText = this.Document.GetText(curLine.Offset, curLine.Length);

                                        var fstSelectedLine = this.Document.GetLineByOffset(this.SelectionStart);

                                        this.Select(fstSelectedLine.Offset, textArea.Selection.Length + (this.SelectionStart - fstSelectedLine.Offset));
                                        textArea.PerformTextInput("");

                                        e.Handled = true;
                                    }
                                }
                                else
                                {
                                    // 删除成对括号
                                    if (this.SelectedText.Length == 0)
                                    {
                                        var leftText = this.LeftText;
                                        var rightText = this.RightText;
                                        if (leftText.EndsWith("(") && rightText.StartsWith(")"))
                                        {
                                            this.Document.Replace(this.SelectionStart - 1, 2, "");
                                            e.Handled = true;
                                        }
                                    }
                                }
                            }
                            break;
                        }
                    case Key.Enter://回车键
                        {
                            if (isCtrl)
                            {
                                if (!isShift)
                                {
                                    //在当前行下方插入一个新行，并移动光标。
                                    var fstLine = this.Document.GetLineByOffset(this.SelectionStart);
                                    var lstLine = this.Document.GetLineByOffset(this.SelectionEnd);
                                    if (fstLine.LineNumber == lstLine.LineNumber)
                                    {
                                        this.Select(fstLine.EndOffset, 0);
                                    }

                                    e.Handled = BreakLine();
                                    AppendANewLine();
                                }
                            }
                            else
                            {
                                if (isShift)
                                {
                                    //在当前行上方插入一个新行，并移动光标。
                                    InsertANewLine();
                                    e.Handled = true;
                                }
                                else
                                {
                                    if (completionWindow != null && completionWindow.Visibility == Visibility.Visible)
                                        break;

                                    e.Handled = BreakLine();
                                }

                                //AvalonEdit有个默认行为：若一行文本以空格开头，无论在第一个非空格字符前按下回车，还是在这些空格前按下回车，
                                //新行开头都不会保留空格！！！
                            }
                            break;
                        }
                    case Key.Tab://Tab键
                        {
                            if (!isCtrl && !isAlt)
                            {
                                if (isShift)
                                {
                                    if (JumpOverMarks(ref e, isShift)) return;
                                }
                                else
                                {
                                    //跳过反引号，用在：按Ctrl+`输入一对反引号并将插入点置于两个反引号之间，输入一些文本后直接跳过右反引号继续输入其它文本。
                                    //这比按两个组合方向按键跳过去或者按方向键跳过去更方便。
                                    //*号对与_对情形类似。

                                    if (JumpOverMarks(ref e, isShift)) return;
                                }
                            }
                            break;
                        }
                    case Key.Z:
                        {
                            if (!isAlt)
                            {
                                if (!isShift && isCtrl)
                                {
                                    if (this.CanUndo) this.Undo();
                                    e.Handled = true;
                                }
                            }
                            break;
                        }
                    case Key.Y:
                        {
                            if (!isAlt && !isShift && isCtrl)
                            {
                                if (this.CanRedo) this.Redo();
                                e.Handled = true;
                            }
                            break;
                        }
                    case Key.C:
                        {
                            if (isCtrl && isShift && !isAlt)
                            {
                                if (this.SelectedText.Length <= 0)
                                {
                                    // 复制当前行到新行
                                    var selLine = this.Document.GetLineByOffset(this.SelectionStart);
                                    var selLineText = this.Document.GetText(selLine.Offset, selLine.Length);
                                    var oldSelOffset = this.SelectionStart - selLine.Offset;
                                    if (selLineText.EndsWith("\r\n"))
                                    {
                                        this.Select(selLine.EndOffset, 0);
                                        this.SelectedText = selLineText;
                                        this.Select(this.SelectionStart + oldSelOffset, 0);
                                    }
                                    else
                                    {
                                        this.Select(selLine.EndOffset, 0);
                                        this.SelectedText = "\r\n" + selLineText;
                                        this.Select(this.SelectionStart + oldSelOffset + 2, 0);
                                    }
                                    e.Handled = true;
                                }
                            }
                            break;
                        }
                    case Key.J:
                        {
                            if (isCtrl && !isShift && !isAlt)
                            {
                                ShowCompletionWindowManual(e);
                                e.Handled = true;
                            }
                            break;
                        }
                    case Key.E:
                        {
                            if (isCtrl && !isShift && !isAlt)
                            {
                                EnableAutoComplete = !EnableAutoComplete;
                            }
                            break;
                        }
                    case Key.OemQuestion:
                        {
                            if (isCtrl && !isShift && !isAlt)
                            {
                                CommentSelectedLines();
                            }
                            break;
                        }
                    case Key.Delete:
                        {
                            if (!isCtrl && !isShift && !isAlt)
                            {
                                if (this.SelectedText == "")
                                {
                                    var leftTxt = this.LeftText;
                                    var rightTxt = this.RightText;
                                    if ((LeftText.EndsWith("\"") && RightText.StartsWith("\"")) ||
                                        (LeftText.EndsWith("\'") && RightText.StartsWith("\'")) ||
                                        (LeftText.EndsWith("(") && RightText.StartsWith(")")) ||
                                        (LeftText.EndsWith("[") && RightText.StartsWith("]")) ||
                                        (LeftText.EndsWith("{") && RightText.StartsWith("}")))
                                    {
                                        this.Select(this.SelectionStart - 1, 2);
                                        this.SelectedText = "";

                                        EnableAutoComplete = true;

                                        e.Handled = true;
                                        break;
                                    }
                                }
                            }
                            break;
                        }
                }
            }
            catch (Exception ex)
            {
                LMessageBox.Show("发生意外错误，无法执行操作。建议保存文件并重启程序。\r\n" +
                    ex.Message + "\r\n" + ex.StackTrace, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
            }
        }

        internal void CommentSelectedLines()
        {
            var fstLine = this.Document.GetLineByOffset(this.SelectionStart);
            var lastLine = this.Document.GetLineByOffset(this.SelectionStart + this.SelectionLength);

            var oldSel = this.SelectionStart;
            var oldSelLen = this.SelectionLength;

            var count = 0;
            for (int i = lastLine.LineNumber; i >= fstLine.LineNumber; i--)
            {
                var line = this.Document.GetLineByNumber(i);
                var lineText = this.Document.GetText(line.Offset, line.Length);
                this.Document.Replace(line.Offset, line.Length, "# " + lineText);
                count += 2;
            }

            if (fstLine.LineNumber == lastLine.LineNumber)
            {
                this.Select(oldSel + 2, oldSelLen);
            }
            else
            {
                this.Select(oldSel + 2, oldSelLen + Math.Max(0, count - 2));
            }
        }

        /// <summary>
        /// 是否启用自动完成。
        /// </summary>
        private bool enableAutoComplete = true;

        public bool EnableAutoComplete
        {
            get { return this.enableAutoComplete; }
            set
            {
                this.enableAutoComplete = value;
                RefreshEnableAutoComplete();
            }
        }

        private void RefreshEnableAutoComplete()
        {
            if (this.enableAutoComplete == false)
            {
                if (this.completionWindow != null && this.completionWindow.Visibility == Visibility.Visible)
                {
                    this.completionWindow.Close();
                }
            }

            OnEnableAutoCompleteChanged(new EnableAutoCompleteEventArgs() { NewValue = this.enableAutoComplete });

            if (this.enableAutoComplete)
            {
                ShowCompletionWindowManual(null);
            }
        }

        public EventHandler<EnableAutoCompleteEventArgs> EnableAutoCompleteChanged;

        protected void OnEnableAutoCompleteChanged(EnableAutoCompleteEventArgs e)
        {
            if (EnableAutoCompleteChanged != null)
            {
                EnableAutoCompleteChanged(this, e);
            }
        }

        internal void ShowCompletionWindowManual(EventArgs e)
        {
            var line = this.Document.GetLineByOffset(this.SelectionStart);
            if (line == null) return;

            var currentOffsetInLine = this.SelectionStart - line.Offset;
            var lineLeftText = this.Document.GetText(line.Offset, currentOffsetInLine);

            if (lineLeftText.StartsWith("#")/* || lineLeftText.Trim(new char[] { ' ', '\t', }).Length <= 0*/) return;

            completionWindow = new CompletionWindow(this.TextArea)
            {
                MinHeight = 34,
                MinWidth = 260,
            };

            IList<ICompletionData> data = CompletionWindow.CompletionList.CompletionData;

            // 根据情况生成自动完成窗口及其中的项目
            if (ShowReporterCommandCompletion(line, lineLeftText, " ", ref data)) return;

            if (data.Count > 0)
            {
                // 完成插入，销毁自动完成窗口
                CompletionWindow.CompletionList.RequestInsertion(e);
                CompletionWindow.Close();
                completionWindow = null;
            }
            else
            {
                if (CompletionWindow != null)
                {
                    CompletionWindow.Hide();
                }
            }
        }

        private CompletionWindow completionWindow;
        /// <summary>
        /// 自动完成窗口。
        /// </summary>
        public CompletionWindow CompletionWindow
        {
            get
            {
                return completionWindow;
            }
        }

        private List<PythonModelInfo> pythonModelInfos = new List<PythonModelInfo>();

        private PythonModelInfo GetPythonModelInfo(string pyModelName)
        {
            if (string.IsNullOrWhiteSpace(pyModelName)) return null;

            foreach (var pi in pythonModelInfos)
            {
                if (pi.ModelName != null && pi.ModelName == pyModelName)
                    return pi;
            }
            return null;
        }

        #endregion 自动完成

        private Regex RegReporterCommand = new Regex(@"^[;；][^;；:：]{0,}[:：]");

        /// <summary>
        /// 附加新行。
        /// </summary>
        private bool BreakLine()
        {
            var selStartOffset = this.SelectionStart;
            var fstLine = this.Document.GetLineByOffset(selStartOffset);

            var selEndOffset = this.SelectionStart + this.SelectionLength;
            var lstLine = this.Document.GetLineByOffset(selEndOffset);

            if (this.SelectionLength == 0)
            {
                var lineText = this.Document.GetText(fstLine.Offset, fstLine.Length);
                var regNewPage = new Regex(@"(?<=(^[;；][ 　\t]{0,}))(新页面|行号前进)(?=([ 　\t]{0,}?$))");
                var matchCmd = regNewPage.Match(lineText);
                if (matchCmd.Success)
                {
                    this.Document.Replace(fstLine.Offset, fstLine.Length, $"；{matchCmd.Value}：");
                }
                else
                {
                    var leftTxt = this.left_text;
                    if (leftTxt != null)
                    {
                        var matchReportCommand = RegReporterCommand.Match(leftTxt);
                        if (matchReportCommand.Success)
                        {
                            var cmdHeader = matchReportCommand.Value.Replace(";", "；").Replace(":", "：");
                            var cmdTail = leftTxt.Substring(matchReportCommand.Length);

                            var matchLocation = ReporterWidget.RegLocation.Match(cmdTail);
                            if (matchLocation.Success)
                            {
                                cmdTail = cmdTail.Substring(0, matchLocation.Index) + ReporterWidget.FormatLocationText(matchLocation.Value) + cmdTail.Substring(matchLocation.Index + matchLocation.Length);
                            }

                            this.Document.Replace(fstLine.Offset, leftTxt.Length, cmdHeader + cmdTail);
                        }
                    }
                }
            }
            return false;
        }

        /// <summary>
        /// 附加新行。
        /// </summary>
        private void AppendANewLine()
        {
            var curLine = this.Document.GetLineByOffset(this.SelectionStart + this.SelectionLength);
            var text = this.Document.GetText(curLine.Offset, curLine.Length);

            this.Document.Insert(curLine.EndOffset, "\r\n");
            this.Select(curLine.EndOffset + 2, 0);
        }

        /// <summary>
        /// 插入新行。
        /// </summary>
        private void InsertANewLine()
        {
            var curLine = this.Document.GetLineByOffset(this.SelectionStart);
            var text = this.Document.GetText(curLine.Offset, curLine.Length);

            this.Document.Insert(curLine.Offset, "\r\n");
            this.Select(curLine.Offset, 0);
        }

        /// <summary>
        /// 根据选项构建正则表达式。
        /// </summary>
        /// <param name="textToFind">要查找的文本</param>
        /// <param name="leftToRight">正序还是逆序。</param>
        private Regex GetRegEx(string textToFind, bool leftToRight)
        {
            RegexOptions options = RegexOptions.None;
            if (leftToRight)
            {
                options |= RegexOptions.RightToLeft;
            }
            options |= RegexOptions.IgnoreCase;

            string pattern = textToFind; // Regex.Escape(textToFind);
            return new Regex(pattern, options);
        }

        /// <summary>
        /// 跳转到下一个输入区域。这些输入区域包括：成对的小括弧之间，成对的大括弧之间，成对的中括弧之间等等。
        /// </summary>
        /// <param name="e"></param>
        private bool JumpOverMarks(ref KeyEventArgs e, bool isShift)
        {
            if (SelectionLength != 0) return false;
            //只有当插入点正好在某些特殊符号（及符号组合）前面时才起作用，用来跳过这些个特殊符号组合。

            try
            {
                var regexText = "(</?[a-zA-Z]{1,}( [^><]*)?>)|(\\*\\*)|(\\[\\=)|(\\=\\])|(__)|([ 　]*?[,，][ 　]*)|r[\"']|([_\\*“”‘’'\"`<>《》〈〉\\(\\)\\[\\]\\{\\}（）〔〕〈〉「」『』〖〗【】［］｛｝＂＇°])";

                Regex regex = GetRegEx(regexText, isShift);
                int start = regex.Options.HasFlag(RegexOptions.RightToLeft) ? SelectionStart : SelectionStart + SelectionLength;
                Match match = regex.Match(Text, start);

                if (!match.Success)  // start again from beginning or end
                {
                    if (regex.Options.HasFlag(RegexOptions.RightToLeft))
                        match = regex.Match(Text, Text.Length);
                    else
                        match = regex.Match(Text, 0);
                }

                if (match.Success)
                {
                    if (isShift)
                    {
                        if (match.Index + match.Length != SelectionStart) return false;

                        var destSel = match.Index;
                        if (destSel >= 0)
                        {
                            Select(destSel, 0);
                            TextLocation loc = Document.GetLocation(match.Index);
                            ScrollTo(loc.Line, loc.Column);

                            e.Handled = true;
                            return true;
                        }
                    }
                    else
                    {
                        if (match.Index != SelectionStart + SelectionLength) return false;

                        var destSel = match.Index + match.Length;
                        if (destSel <= Document.TextLength)
                        {
                            Select(destSel, 0);
                            TextLocation loc = Document.GetLocation(match.Index);
                            ScrollTo(loc.Line, loc.Column);

                            e.Handled = true;
                            return true;
                        }
                    }

                    return false;
                }

                return false;
            }
            catch (Exception ex)
            {
                LMessageBox.Show(ex.Message, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return false;
            }
        }

        /// <summary>
        /// [覆盖方法]只有用这个办法才能使Alt+Up/Down生效。
        /// </summary>
        protected override void OnKeyDown(KeyEventArgs e)
        {
            if (e.KeyboardDevice.Modifiers == ModifierKeys.Alt
                && (e.KeyboardDevice.IsKeyDown(Key.RightAlt) || e.KeyboardDevice.IsKeyDown(Key.LeftAlt)))
            {
                if ((e.Key == Key.Up || e.SystemKey == Key.Up))
                {
                    SwapWithPreviewLine();
                    e.Handled = true;
                }
                else if ((e.Key == Key.Down || e.SystemKey == Key.Down))
                {
                    SwapWithNextLine();
                    e.Handled = true;
                }
            }
            base.OnKeyDown(e);
        }

        /// <summary>
        /// 与后一行交换位置。
        /// </summary>
        internal void SwapWithNextLine()
        {
            var curStartLine = this.Document.GetLineByOffset(this.SelectionStart);
            var curEndLine = this.Document.GetLineByOffset(this.SelectionStart + this.SelectionLength);
            var nextLine = curEndLine.NextLine;

            if (nextLine != null)
            {
                this.BeginChange();
                var lineSplitter = "\n";
                if (this.Document.Text.Contains("\r\n"))
                {
                    lineSplitter = "\r\n";
                }
                var nextLineText = this.Document.GetText(nextLine.Offset, nextLine.Length);
                this.Document.Remove(nextLine.Offset - lineSplitter.Length, nextLine.Length + lineSplitter.Length);//先删除下一行

                var curStartLineOffset = curStartLine.Offset;
                this.Document.Insert(curStartLineOffset, nextLineText + lineSplitter);
                this.EndChange();
            }
        }

        /// <summary>
        /// 与前一行交换位置。
        /// </summary>
        internal void SwapWithPreviewLine()
        {
            var curStartLine = this.Document.GetLineByOffset(this.SelectionStart);
            var curEndLine = this.Document.GetLineByOffset(this.SelectionStart + this.SelectionLength);
            var preLine = curStartLine.PreviousLine;

            if (preLine != null)
            {
                this.BeginChange();
                var preStartLineOffset = preLine.Offset;
                var startOffset = this.SelectionStart - curStartLine.Offset;
                var selLength = this.SelectionLength;

                var lineSplitter = "\n";
                if (this.Document.Text.Contains("\r\n"))
                {
                    lineSplitter = "\r\n";
                }

                var preLineText = this.Document.GetText(preLine.Offset, preLine.Length);
                this.Document.Insert(curEndLine.EndOffset, lineSplitter);
                this.Document.Insert(curEndLine.EndOffset + lineSplitter.Length, preLineText);
                this.Document.Remove(preLine.Offset, preLine.Length + lineSplitter.Length);
                this.EndChange();

                this.Select(preStartLineOffset + startOffset, selLength);
            }
        }

        #region 便于 IronPython 脚本调用的方法

        /// <summary>
        /// SelectedText 的别名。
        /// </summary>
        public string seld { get { return this.SelectedText; } set { this.SelectedText = value; } }
        public string seld_txt { get { return this.SelectedText; } set { this.SelectedText = value; } }
        public string 选择的文本 { get { return this.SelectedText; } set { this.SelectedText = value; } }

        /// <summary>
        /// 活动文本文本，包括从 选择区头部所在行的头部 到 选择区尾部所在行 的尾部间的所有文本。
        /// 它一定是包含 SelectedText 的。
        /// </summary>
        public string actived_text
        {
            get
            {
                return this.Document.GetText(fst_doc_ln.Offset, lst_doc_ln.EndOffset - fst_doc_ln.Offset);
            }
        }
        public string actd_txt => actived_text;
        public string ActivedText => actived_text;
        public string 活动文本 { get { return actived_text; } }

        /// <summary>
        /// 取选择区到行首的文本
        /// </summary>
        public string LeftText
        {
            get
            {
                var selOffset = this.SelectionStart;
                var fstLine = this.Document.GetLineByOffset(selOffset);
                if (fstLine == null) return null;
                return this.Document.GetText(fstLine.Offset, selOffset - fstLine.Offset);
            }
        }
        /// <summary>
        /// LeftText 的别名。
        /// </summary>
        public string lt { get { return LeftText; } }
        public string l_txt { get { return LeftText; } }
        public string lt_txt { get { return LeftText; } }
        public string lft_txt { get { return LeftText; } }
        public string left_txt { get { return LeftText; } }
        public string left_text { get { return LeftText; } }
        public string 选择区左侧文本 { get { return LeftText; } }

        /// <summary>
        /// 取选择区尾到行尾的文本。
        /// </summary>
        public string RightText
        {
            get
            {
                var selEndOffset = this.SelectionStart + this.SelectionLength;
                var lastLine = this.Document.GetLineByOffset(selEndOffset);
                if (lastLine == null) return null;
                return this.Document.GetText(selEndOffset, lastLine.EndOffset - selEndOffset);
            }
        }
        /// <summary>
        /// RightText 的别名。
        /// </summary>
        public string rt { get { return RightText; } }
        public string r_txt { get { return RightText; } }
        public string rt_txt { get { return RightText; } }
        public string rgt_txt { get { return RightText; } }
        public string right_txt { get { return RightText; } }
        public string right_text { get { return RightText; } }
        public string 选择区右侧文本 { get { return RightText; } }

        /// <summary>
        /// 选择头。指选择区头到它所在行的行尾的文本。
        /// </summary>
        public string SelectedHead
        {
            get
            {
                var selOffset = this.SelectionStart;
                var fstLine = this.Document.GetLineByNumber(selOffset);
                if (fstLine == null) return null;
                return this.Document.GetText(selOffset, fstLine.EndOffset);
            }
        }
        /// <summary>
        /// SelectedHead 的别名。
        /// </summary>
        public string sdh { get { return SelectedHead; } }
        public string seld_head { get { return SelectedHead; } }
        public string selected_head { get { return SelectedHead; } }
        public string ht { get { return SelectedHead; } }
        public string hd_txt { get { return SelectedHead; } }
        public string head_txt { get { return SelectedHead; } }
        public string head_text { get { return SelectedHead; } }
        public string 选择区头到行尾 { get { return SelectedHead; } }

        /// <summary>
        /// 选定文本尾部到行首的文本。
        /// </summary>
        public string SelectedTail
        {
            get
            {
                var selEndOffset = this.SelectionStart + this.SelectionLength;
                var lstLine = this.Document.GetLineByOffset(selEndOffset);
                if (lstLine == null) return null;
                return this.Document.GetText(lstLine.Offset, selEndOffset - lstLine.Offset);
            }
        }
        /// <summary>
        /// SelectedTail 的别名。
        /// </summary>
        public string sdt { get { return SelectedTail; } }
        public string seld_tail { get { return SelectedTail; } }
        public string selected_tail { get { return SelectedTail; } }
        public string tt { get { return SelectedTail; } }
        public string tail_txt { get { return SelectedTail; } }
        public string tail_text { get { return SelectedTail; } }
        public string 选择区尾到行首 { get { return SelectedTail; } }

        /// <summary>
        /// 选择区第一行
        /// </summary>
        public DocumentLine first_document_line { get { return this.Document.GetLineByOffset(this.SelectionStart); } }
        /// <summary>
        /// first_line 别名。
        /// </summary>
        public DocumentLine fst_doc_ln { get { return first_document_line; } }
        /// <summary>
        /// 选择区最后一行。
        /// </summary>
        public DocumentLine last_document_line { get { return this.Document.GetLineByOffset(this.SelectionStart + this.SelectionLength); } }
        /// <summary>
        /// last_line 别名。
        /// </summary>
        public DocumentLine lst_doc_ln { get { return last_document_line; } }
        /// <summary>
        /// 选择区第一行文本。
        /// </summary>
        public string first_line
        {
            get
            {
                if (first_document_line == null || first_document_line.Offset < 0 || first_document_line.EndOffset >= this.Text.Length) return null;
                return this.Document.GetText(first_document_line.Offset, first_document_line.Length);
            }
        }
        /// <summary>
        /// first_line 别名。
        /// </summary>
        public string fst_ln { get { return first_line; } }
        public string 选择区头部所在行文本 { get { return first_line; } }

        /// <summary>
        /// 选择区最后一行文本。
        /// </summary>
        public string last_line
        {
            get
            {
                if (last_document_line == null || last_document_line.Offset < 0 || last_document_line.EndOffset >= this.Text.Length) return null;
                return this.Document.GetText(last_document_line.Offset, last_document_line.Length);
            }
        }
        /// <summary>
        /// last_line_text 别名。
        /// </summary>
        public string lst_ln { get { return last_line; } }
        public string 选择区尾部所在行文本 { get { return last_line; } }

        /// <summary>
        /// 选择区前一行。PreviewLine() 方法的别名。
        /// </summary>
        public DocumentLine preview_document_line
        {
            get
            {
                try
                {
                    var fst_line = this.Document.GetLineByOffset(this.SelectionStart);
                    return this.Document.GetLineByNumber(fst_line.LineNumber - 1);
                }
                catch { return null; }
            }
        }

        public DocumentLine pre_doc_ln => preview_document_line;

        /// <summary>
        /// 选择区前一行的文本。
        /// </summary>
        public string preview_line
        {
            get
            {
                if (preview_document_line == null) return null;
                return this.Document.GetText(preview_document_line.Offset, preview_document_line.Length);
            }
        }
        /// <summary>
        /// preview_line_text 别名。
        /// </summary>
        public string pre_ln { get { return preview_line; } }
        public string pl => preview_line;
        public string p_l => preview_line;
        public string 选择区头部前一行文本 { get { return preview_line; } }

        /// <summary>
        /// 选择区后一行。 NextLine() 方法的别名。
        /// </summary>
        public DocumentLine next_document_line
        {
            get
            {
                try
                {
                    var lst_line = this.Document.GetLineByOffset(this.SelectionStart + this.SelectionLength);
                    return this.Document.GetLineByNumber(lst_line.LineNumber + 1);
                }
                catch { return null; }
            }
        }
        /// <summary>
        /// next_line 别名。
        /// </summary>
        public DocumentLine nxt_doc_ln { get { return next_document_line; } }
        /// <summary>
        /// 选择区后一行的文本。
        /// </summary>
        public string next_line
        {
            get
            {
                if (next_document_line == null) return null;
                return this.Document.GetText(next_document_line.Offset, next_document_line.Length);
            }
        }
        /// <summary>
        /// next_line 别名。
        /// </summary>
        public string nxt_ln { get { return next_line; } }
        public string nl => next_line;
        public string n_l => next_line;
        public string 选择区尾部后一行文本 { get { return next_line; } }

        /// <summary>
        /// 选择文本。是 Select() 方法的别名。
        /// </summary>
        public void sel(int start, int length)
        {
            this.Select(start, length);
        }

        public void 选择文本(int start, int length)
        {
            this.Select(start, length);
        }

        /// <summary>
        /// 选取选择区起始位置到起始位置所在行行首之间的文本。
        /// </summary>
        public void SelectLeft()
        {
            var fstline = fst_doc_ln;
            this.Select(fstline.Offset, SelectionStart - fstline.Offset);
        }
        /// <summary>
        /// 选取选择区起始位置到起始位置所在行行首之间的文本。
        /// </summary>
        public void sel_left() => SelectLeft();
        public void sel_lft() => SelectLeft();
        public void 选择区头选到行首() => SelectLeft();

        /// <summary>
        /// 选取选择区开始位置到结束位置所在行行尾之间的文本。
        /// </summary>
        public void SelectRight()
        {
            var lstline = lst_doc_ln;
            this.Select(SelectionStart, lstline.EndOffset - SelectionStart);
        }
        /// <summary>
        /// 选取选择区开始位置到结束位置所在行行尾之间的文本。
        /// </summary>
        public void sel_right() => SelectRight();
        public void sel_rgt() => SelectRight();
        public void 选择区尾选到行尾() => SelectRight();

        /// <summary>
        /// 选取选择区头文本。
        /// </summary>
        public void SelectHead()
        {
            var fstline = fst_doc_ln;
            this.Select(SelectionStart, fstline.EndOffset - SelectionStart);
        }
        public void sel_head() => SelectHead();
        public void 选择区头选到行尾() => SelectHead();

        /// <summary>
        /// 选取选择区尾文本。
        /// </summary>
        public void SelectTail()
        {
            var lstline = lst_doc_ln;
            this.Select(lstline.Offset, SelectionStart + SelectionLength - lstline.Offset);
        }
        public void sel_tail() => SelectTail();
        public void 选择区尾选到行头() => SelectTail();

        /// <summary>
        /// 选择区头部索引。
        /// </summary>
        public int sel_start { get { return SelectionStart; } }
        public int 选择区头部索引 { get { return SelectionStart; } }

        /// <summary>
        /// 选择区尾部索引。
        /// </summary>
        public int SelectionEnd { get { return SelectionStart + SelectionLength; } }
        /// <summary>
        /// 选择区尾部索引别名。
        /// </summary>
        public int sel_end { get { return SelectionEnd; } }
        public int 选择区尾部索引 { get { return SelectionEnd; } }

        /// <summary>
        /// 选择区长度。
        /// </summary>
        public int sel_len { get { return SelectionLength; } }
        public int 选择区长度 { get { return SelectionLength; } }

        /// <summary>
        /// 选中前一行文本。
        /// </summary>
        /// <returns>如果不存在前一行，返回 false。</returns>
        public bool SelectPreviewLine()
        {
            try
            {
                if (preview_document_line == null) return false;
                this.Select(preview_document_line.Offset, preview_document_line.Length);
                return true;
            }
            catch { return false; }
        }
        public bool sel_pre_ln() => SelectPreviewLine();
        public bool 选择前一行文本() => SelectPreviewLine();

        /// <summary>
        /// 选中后一行文本。
        /// </summary>
        /// <returns>如果不存在后一行，返回 false。</returns>
        public bool SelectNextLine()
        {
            try
            {
                if (next_document_line == null) return false;
                this.Select(next_document_line.Offset, next_document_line.Length);
                return true;
            }
            catch { return false; }
        }
        public bool sel_nxt_ln() => SelectNextLine();
        public bool 选择后一行文本() => SelectNextLine();

        /// <summary>
        /// 选择区涉及到的所有行。
        /// </summary>
        public List<DocumentLine> selected_document_lines
        {
            get
            {
                try
                {
                    var list = new List<DocumentLine>();
                    for (int i = fst_doc_ln.LineNumber; i <= lst_doc_ln.LineNumber; i++)
                    {
                        list.Add(this.Document.GetLineByNumber(i));
                    }
                    return list;
                }
                catch { return null; }
            }
        }
        public List<DocumentLine> seld_doc_lns => selected_document_lines;
        /// <summary>
        /// 活动文本所涉及到的所有行——这种情况下它和 selected_document_line 的结果是相同的。
        /// 因为活动文本（active_text）是指从选择区头部延伸到所在行的头部，并从选择区尾部延伸到所在行尾部。
        /// </summary>
        public List<DocumentLine> actived_document_lines => selected_document_lines;
        public List<DocumentLine> actd_doc_lns => selected_document_lines;

        /// <summary>
        /// 选中的文本。
        /// </summary>
        /// <returns></returns>
        public string[] selected_lines
        {
            get
            {
                return this.SelectedText.Split(new string[] { "\r\n", }, StringSplitOptions.None);
            }
        }
        public string[] seld_lns => selected_lines;
        public string[] 选择的所有文本行 => selected_lines;

        public string[] actived_lines
        {
            get { return this.actived_text.Split(new string[] { "\r\n", }, StringSplitOptions.None); }
        }
        public string[] actd_lns => actived_lines;
        public string[] 活动的所有文本行 => actived_lines;

        public Tuple<int, int> 取行起始位置与长度(int lineNumber)
        {
            if (lineNumber < 1) lineNumber = 1;
            if (lineNumber > this.Document.LineCount) lineNumber = this.Document.LineCount;

            var dLine = this.Document.GetLineByNumber(lineNumber);
            return new Tuple<int, int>(dLine.Offset, dLine.Length);
        }

        public string 取文本(Tuple<int, int> tuple)
        {
            return this.Document.GetText(tuple.Item1, tuple.Item2);
        }

        #endregion 便于 IronPython 脚本调用的方法
    }

}
